home *** CD-ROM | disk | FTP | other *** search
/ Best of www.BestZips.com (Collector's Edition) / Best of WWW.BESTZIPS.COM Collector's Edition (JCSM Shareware) (JCS Marketing).ISO / editors_ / ze16v250.zip / CTAGS.C_ / CTAGS.C
C/C++ Source or Header  |  1996-01-18  |  23KB  |  910 lines

  1. /***************************************************************************
  2.  
  3.  
  4.    General Information and Notes Regarding the CTAGS.C File
  5.    ========================================================
  6.  
  7.    This file has been provided as an example of how to write a MS-DOS utility
  8.    that can interface to the Zeus Tool Manager interface. Some very minor 
  9.    changes have been made to make this code work with Zeus. All changes have
  10.    been marked using the initalis JAJ. This file is one of the files that make
  11.    up the Elvis editor which is a VI clone (for more information see the Elvis
  12.    editor refer to the README.TXT which is also shown below).
  13.  
  14.    To compile this file refer to the CTAGS.MAK make or CTAGS.IDE project file
  15.    supplied in the EXAMPLES directory. To run the the program you can use 
  16.    the CTAGS.EXE from the command line of just open an valid C or C++ file and
  17.    select the Zeus Tools CTAGS Utility menu item.
  18.  
  19.    
  20.    ---------------- JAJ - Copy of the original Elvis README.TXT ------------
  21.  
  22.    Elvis is a clone of vi/ex, the standard UNIX editor.  Elvis supports
  23.    nearly all of the vi/ex commands, in both visual mode and colon mode.
  24.    
  25.    Elvis runs under BSD UNIX, AT&T SysV UNIX, SCO Xenix, Minix, MS-DOS
  26.    (Turbo-C or MSC 5.1), Atari TOS, OS9/68000, Coherent, VMS, and AmigaDos.
  27.    Ports to other operating systems are in progress; contact me before you
  28.    start porting it to some other OS, because somebody else may have
  29.    already done it for you.
  30.    
  31.    Elvis is freely redistributable, in either source form or executable
  32.    form.  There are no restrictions on how you may use it.
  33.    
  34.    The file "elvisman.txt" contains the manual for elvis.  It is a plain
  35.    ASCII file with nothing more exotic than a newline character.  It is
  36.    formatted for 66-line, 80-column pages.  There may also be an archive of
  37.    "*.ms" and "*.man" files, which contain the TROFF source text used to
  38.    generate that manual.
  39.    
  40.    The file named "Makefile.mix" is used to compile elvis for all systems
  41.    except VMS and possibly MS-DOS.  You should copy "Makefile.mix" to
  42.    "Makefile", and then edit "Makefile" to select the appropriate group of
  43.    settings for your system.
  44.    
  45.    Author: Steve Kirkendall
  46.        14407 SW Teal Blvd. #C
  47.        Beaverton, OR   97005
  48.    
  49.    E-mail:    kirkenda@cs.pdx.edu
  50.    
  51.    Phone:    (503) 643-6980
  52.    
  53. ****************************************************************************/
  54.  
  55. /* ctags.c */
  56.  
  57. /* This is a reimplementation of the ctags(1) program.  It supports ANSI C,
  58.  * and has heaps o' flags.  It is meant to be distributed with elvis.
  59.  */
  60.  
  61. #include <stdio.h>
  62. #include <string.h>
  63. #include "config.h"
  64. #ifndef FALSE
  65. # define FALSE    0
  66. # define TRUE    1
  67. #endif
  68. #ifndef TAGS
  69. # define TAGS    "tags"
  70. #endif
  71. #ifndef REFS
  72. # define REFS    "refs"
  73. #endif
  74. #ifndef BLKSIZE
  75. # define BLKSIZE 1024
  76. #endif
  77.  
  78. #include "ctype.c" /* yes, that really is the .c file, not the .h one. */
  79.  
  80. /* -------------------------------------------------------------------------- */
  81. /* Some global variables */
  82.  
  83. /* The following boolean variables are set according to command line flags */
  84. int    incl_static;    /* -s  include static tags */
  85. int    incl_types;    /* -t  include typedefs and structs */
  86. int    incl_vars;    /* -v  include variables */
  87. int    make_refs;    /* -r  generate a "refs" file */
  88. int    append_files;    /* -a  append to "tags" [and "refs"] files */
  89.  
  90. /* The following are used for outputting to the "tags" and "refs" files */
  91. FILE    *tags;        /* used for writing to the "tags" file */
  92. FILE    *refs;        /* used for writing to the "refs" file */
  93.  
  94. /* -------------------------------------------------------------------------- */
  95. /* These are used for reading a source file.  It keeps track of line numbers */
  96. char    *file_name;    /* name of the current file */
  97. FILE    *file_fp;    /* stream used for reading the file */
  98. long    file_lnum;    /* line number in the current file */
  99. long    file_seek;    /* fseek() offset to the start of current line */
  100. int    file_afternl;    /* boolean: was previous character a newline? */
  101. int    file_prevch;    /* a single character that was ungotten */
  102. int    file_header;    /* boolean: is the current file a header file? */
  103.  
  104. /* This function opens a file, and resets the line counter.  If it fails, it
  105.  * it will display an error message and leave the file_fp set to NULL.
  106.  */
  107. void file_open(name)
  108.     char    *name;    /* name of file to be opened */
  109. {
  110.     /* if another file was already open, then close it */
  111.     if (file_fp)
  112.     {
  113.         fclose(file_fp);
  114.     }
  115.  
  116.     /* try to open the file for reading.  The file must be opened in
  117.      * "binary" mode because otherwise fseek() would misbehave under DOS.
  118.      */
  119. #if MSDOS || TOS
  120.     file_fp = fopen(name, "rb");
  121. #else
  122.     file_fp = fopen(name, "r");
  123. #endif
  124.     if (!file_fp)
  125.     {
  126.         perror(name);
  127.         //- JAJ Added error output to stdout so Zeus will see it
  128.         printf("Error opening file '%s'\n", name);
  129.     }
  130.  
  131.     /* reset the name & line number */
  132.     file_name = name;
  133.     file_lnum = 0L;
  134.     file_seek = 0L;
  135.     file_afternl = TRUE;
  136.  
  137.     /* determine whether this is a header file */
  138.     file_header = FALSE;
  139.     name += strlen(name) - 2;
  140.     if (name >= file_name && name[0] == '.' && (name[1] == 'h' || name[1] == 'H'))
  141.     {
  142.         file_header = TRUE;
  143.     }
  144. }
  145.  
  146. /* This function reads a single character from the stream.  If the *previous*
  147.  * character was a newline, then it also increments file_lnum and sets
  148.  * file_offset.
  149.  */
  150. int file_getc()
  151. {
  152.     int    ch;
  153.  
  154.     /* if there is an ungotten character, then return it.  Don't do any
  155.      * other processing on it, though, because we already did that the
  156.      * first time it was read.
  157.      */
  158.     if (file_prevch)
  159.     {
  160.         ch = file_prevch;
  161.         file_prevch = 0;
  162.         return ch;
  163.     }
  164.  
  165.     /* if previous character was a newline, then we're starting a line */
  166.     if (file_afternl)
  167.     {
  168.         file_afternl = FALSE;
  169.         file_seek = ftell(file_fp);
  170.         file_lnum++;
  171.     }
  172.  
  173.     /* Get a character.  If no file is open, then return EOF */
  174.     ch = (file_fp ? getc(file_fp) : EOF);
  175.  
  176.     /* if it is a newline, then remember that fact */
  177.     if (ch == '\n')
  178.     {
  179.         file_afternl = TRUE;
  180.     }
  181.  
  182.     /* return the character */
  183.     return ch;
  184. }
  185.  
  186. /* This function ungets a character from the current source file */
  187. void file_ungetc(ch)
  188.     int    ch;    /* character to be ungotten */
  189. {
  190.     file_prevch = ch;
  191. }
  192.  
  193. /* This function copies the current line out some other fp.  It has no effect
  194.  * on the file_getc() function.  During copying, any '\' characters are doubled
  195.  * and a leading '^' or trailing '$' is also quoted.  The newline character is
  196.  * not copied.
  197.  *
  198.  * This is meant to be used when generating a tag line.
  199.  */
  200. void file_copyline(seek, fp)
  201.     long    seek;    /* where the lines starts in the source file */
  202.     FILE    *fp;    /* the output stream to copy it to */
  203. {
  204.     long    oldseek;/* where the file's pointer was before we messed it up */
  205.     char    ch;    /* a single character from the file */
  206.     char    next;    /* the next character from this file */
  207.  
  208.     /* go to the start of the line */
  209.     oldseek = ftell(file_fp);
  210.     fseek(file_fp, seek, 0);
  211.  
  212.     /* if first character is '^', then emit \^ */
  213.     ch = getc(file_fp);
  214.     if (ch == '^')
  215.     {
  216.         putc('\\', fp);
  217.         putc('^', fp);
  218.         ch = getc(file_fp);
  219.     }
  220.  
  221.     /* write everything up to, but not including, the newline */
  222.     while (ch != '\n')
  223.     {
  224.         /* preread the next character from this file */
  225.         next = getc(file_fp);
  226.  
  227.         /* if character is '\', or a terminal '$', then quote it */
  228.         if (ch == '\\' || (ch == '$' && next == '\n'))
  229.         {
  230.             putc('\\', fp);
  231.         }
  232.         putc(ch, fp);
  233.  
  234.         /* next character... */
  235.         ch = next;
  236.     }
  237.  
  238.     /* seek back to the old position */
  239.     fseek(file_fp, oldseek, 0);
  240. }
  241.  
  242. /* -------------------------------------------------------------------------- */
  243. /* This section handles preprocessor directives.  It strips out all of the
  244.  * directives, and may emit a tag for #define directives.
  245.  */
  246.  
  247. int    cpp_afternl;    /* boolean: look for '#' character? */
  248. int    cpp_prevch;    /* an ungotten character, if any */
  249. int    cpp_refsok;    /* boolean: can we echo characters out to "refs"? */
  250.  
  251. /* This function opens the file & resets variables */
  252. void cpp_open(name)
  253.     char    *name;    /* name of source file to be opened */
  254. {
  255.     /* use the lower-level file_open function to open the file */
  256.     file_open(name);
  257.  
  258.     /* reset variables */
  259.     cpp_afternl = TRUE;
  260.     cpp_refsok = TRUE;
  261. }
  262.  
  263. /* This function copies a character from the source file to the "refs" file */
  264. void cpp_echo(ch)
  265.     int    ch; /* the character to copy */
  266. {
  267.     static    wasnl;
  268.  
  269.     /* echo non-EOF chars, unless not making "ref", or echo turned off */
  270.     if (ch != EOF && make_refs && cpp_refsok && !file_header)
  271.     {
  272.         /* try to avoid blank lines */
  273.         if (ch == '\n')
  274.         {
  275.             if (wasnl)
  276.             {
  277.                 return;
  278.             }
  279.             wasnl = TRUE;
  280.         }
  281.         else
  282.         {
  283.             wasnl = FALSE;
  284.         }
  285.  
  286.         /* add the character */
  287.         putc(ch, refs);
  288.     }
  289. }
  290.  
  291. /* This function returns the next character which isn't part of a directive */
  292. int cpp_getc()
  293. {
  294.     static
  295.     int    ch;    /* the next input character */
  296.     char    *scan;
  297.  
  298.      //- JAJ added
  299.    int   count;
  300.  
  301.     /* if we have an ungotten character, then return it */
  302.     if (cpp_prevch)
  303.     {
  304.         ch = cpp_prevch;
  305.         cpp_prevch = 0;
  306.         return ch;
  307.     }
  308.  
  309.     /* Get a character from the file.  Return it if not special '#' */
  310.     ch = file_getc();
  311.     if (ch == '\n')
  312.     {
  313.         cpp_afternl = TRUE;
  314.         cpp_echo(ch);
  315.         return ch;
  316.     }
  317.     else if (ch != '#' || !cpp_afternl)
  318.     {
  319.         /* normal character.  Any non-whitespace should turn off afternl */
  320.         if (ch != ' ' && ch != '\t')
  321.         {
  322.             cpp_afternl = FALSE;
  323.         }
  324.         cpp_echo(ch);
  325.         return ch;
  326.     }
  327.  
  328.     /* Yikes!  We found a directive */
  329.  
  330.     /* see whether this is a #define line */
  331.     scan = " define ";
  332.     while (*scan)
  333.     {
  334.         if (*scan == ' ')
  335.         {
  336.             /* space character matches any whitespace */
  337.             do
  338.             {
  339.                 ch = file_getc();
  340.             } while (ch == ' ' || ch == '\t');
  341.             file_ungetc(ch);
  342.         }
  343.         else
  344.         {
  345.             /* other characters should match exactly */
  346.             ch = file_getc();
  347.             if (ch != *scan)
  348.             {
  349.                 file_ungetc(ch);
  350.                 break;
  351.             }
  352.         }
  353.         scan++;
  354.     }
  355.  
  356.     /* is this a #define line?  and should we generate a tag for it? */
  357.     if (!*scan && (file_header || incl_static))
  358.     {
  359.         /* if not a header, then this will be a static tag */
  360.         if (!file_header)
  361.         {
  362.           //- JAJ change the way the data is displayed
  363. //            fputs(file_name, tags);
  364. //            putc(':', tags);
  365.            fprintf(tags, "%s: ", "define  ");
  366.         }
  367.  
  368.         //- JAJ added
  369.       count = 0;
  370.  
  371.         /* output the tag name */
  372.         for (ch = file_getc(); isalnum(ch) || ch == '_'; ch = file_getc())
  373.         {
  374.             putc(ch, tags);
  375.  
  376.           //- JAJ keep track of the number of chars processed
  377.          count++;
  378.         }
  379.  
  380.       //- JAJ change the way the data is displayed
  381.       if (count < 30)
  382.        for (ch = count; ch < 30; ++ch)
  383.          putc(' ', tags);
  384.  
  385.         /* output a tab, the filename, another tab, and the line number */
  386.       //- JAJ change the way the data is displayed
  387. //        fprintf(tags, "\t%s\t%ld\n", file_name, file_lnum);
  388.         fprintf(tags, " %s\t%ld\n", file_name, file_lnum);
  389.     }
  390.  
  391.     /* skip to the end of the directive -- a newline that isn't preceded
  392.      * by a '\' character.
  393.      */
  394.     while (ch != EOF && ch != '\n')
  395.     {
  396.         if (ch == '\\')
  397.         {
  398.             ch = file_getc();
  399.         }
  400.         ch = file_getc();
  401.     }
  402.  
  403.     /* return the newline that we found at the end of the directive */
  404.     cpp_echo(ch);
  405.     return ch;
  406. }
  407.  
  408. /* This puts a character back into the input queue for the source file */
  409. cpp_ungetc(ch)
  410.     int    ch;    /* a character to be ungotten */
  411. {
  412.     cpp_prevch = ch;
  413. }
  414.  
  415.  
  416. /* -------------------------------------------------------------------------- */
  417. /* This is the lexical analyser.  It gets characters from the preprocessor,
  418.  * and gives tokens to the parser.  Some special codes are...
  419.  *   (deleted)  /*...* /    (comments)
  420.  *   (deleted)    //...\n    (comments)
  421.  *   (deleted)    (*    (parens used in complex declaration)
  422.  *   (deleted)    [...]    (array subscript, when ... contains no ])
  423.  *   (deleted)    struct    (intro to structure declaration)
  424.  *   BODY    {...}    ('{' can occur anywhere, '}' only at BOW if ... has '{')
  425.  *   ARGS    (...{    (args of function, not extern or forward)
  426.  *   ARGS    (...);    (args of an extern/forward function declaration)
  427.  *   COMMA    ,    (separate declarations that have same scope)
  428.  *   SEMICOLON    ;    (separate declarations that have different scope)
  429.  *   SEMICOLON  =...;    (initializer)
  430.  *   TYPEDEF    typedef    (the "typedef" keyword)
  431.  *   STATIC    static    (the "static" keyword)
  432.  *   STATIC    private    (the "static" keyword)
  433.  *   STATIC    PRIVATE    (the "static" keyword)
  434.  *   NAME    [a-z]+    (really any valid name that isn't reserved word)
  435.  */
  436.  
  437. /* #define EOF -1 */
  438. #define DELETED      0
  439. #define BODY      1
  440. #define ARGS      2
  441. #define COMMA      3
  442. #define SEMICOLON 4
  443. #define TYPEDEF   5
  444. #define STATIC      6
  445. #define EXTERN      7
  446. #define NAME      8
  447.  
  448. char    lex_name[BLKSIZE];    /* the name of a "NAME" token */
  449. long    lex_seek;        /* start of line that contains lex_name */
  450.  
  451. lex_gettoken()
  452. {
  453.     int    ch;        /* a character from the preprocessor */
  454.     int    next;        /* the next character */
  455.     int    token;        /* the token that we'll return */
  456.     int    i;
  457.  
  458.     /* loop until we get a token that isn't "DELETED" */
  459.     do
  460.     {
  461.         /* get the next character */
  462.         ch = cpp_getc();
  463.  
  464.         /* process the character */
  465.         switch (ch)
  466.         {
  467.           case ',':
  468.             token = COMMA;
  469.             break;
  470.  
  471.           case ';':
  472.             token = SEMICOLON;
  473.             break;
  474.  
  475.           case '/':
  476.             /* get the next character */
  477.             ch = cpp_getc();
  478.             switch (ch)
  479.             {
  480.               case '*':    /* start of C comment */
  481.                 ch = cpp_getc();
  482.                 next = cpp_getc();
  483.                 while (next != EOF && (ch != '*' || next != '/'))
  484.                 {
  485.                     ch = next;
  486.                     next = cpp_getc();
  487.                 }
  488.                 break;
  489.  
  490.               case '/':    /* start of a C++ comment */
  491.                 do
  492.                 {
  493.                     ch = cpp_getc();
  494.                 } while (ch != '\n' && ch != EOF);
  495.                 break;
  496.  
  497.               default:    /* some other slash */
  498.                 cpp_ungetc(ch);
  499.             }
  500.             token = DELETED;
  501.             break;
  502.  
  503.           case '(':
  504.             ch = cpp_getc();
  505.             if (ch == '*')
  506.             {
  507.                 token = DELETED;
  508.             }
  509.             else
  510.             {
  511.                 next = cpp_getc();
  512.                 while (ch != '{' && ch != EOF && (ch != ')' || next != ';'))/*}*/
  513.                 {
  514.                     ch = next;
  515.                     next = cpp_getc();
  516.                 }
  517.                 if (ch == '{')/*}*/
  518.                 {
  519.                     cpp_ungetc(ch);
  520.                 }
  521.                 else if (next == ';')
  522.                 {
  523.                     cpp_ungetc(next);
  524.                 }
  525.                 token = ARGS;
  526.             }
  527.             break;
  528.  
  529.           case '{':/*}*/
  530.             /* don't send the next characters to "refs" */
  531.             cpp_refsok = FALSE;
  532.  
  533.             /* skip ahead to closing '}', or to embedded '{' */
  534.             do
  535.             {
  536.                 ch = cpp_getc();
  537.             } while (ch != '{' && ch != '}' && ch != EOF);
  538.  
  539.             /* if has embedded '{', then skip to '}' in column 1 */
  540.             if (ch == '{') /*}*/
  541.             {
  542.                 ch = cpp_getc();
  543.                 next = cpp_getc();
  544.                 while (ch != EOF && (ch != '\n' || next != '}'))/*{*/
  545.                 {
  546.                     ch = next;
  547.                     next = cpp_getc();
  548.                 }
  549.             }
  550.  
  551.             /* resume "refs" processing */
  552.             cpp_refsok = TRUE;
  553.             cpp_echo('}');
  554.  
  555.             token = BODY;
  556.             break;
  557.  
  558.           case '[':
  559.             /* skip to matching ']' */
  560.             do
  561.             {
  562.                 ch = cpp_getc();
  563.             } while (ch != ']' && ch != EOF);
  564.             token = DELETED;
  565.             break;
  566.  
  567.           case '=':
  568.               /* skip to next ';' */
  569.             do
  570.             {
  571.                 ch = cpp_getc();
  572.  
  573.                 /* leave array initializers out of "refs" */
  574.                 if (ch == '{')
  575.                 {
  576.                     cpp_refsok = FALSE;
  577.                 }
  578.             } while (ch != ';' && ch != EOF);
  579.  
  580.             /* resume echoing to "refs" */
  581.             if (!cpp_refsok)
  582.             {
  583.                 cpp_refsok = TRUE;
  584.                 cpp_echo('}');
  585.                 cpp_echo(';');
  586.             }
  587.             token = SEMICOLON;
  588.             break;
  589.  
  590.           case EOF:
  591.             token = EOF;
  592.             break;
  593.  
  594.           default:
  595.             /* is this the start of a name/keyword? */
  596.             if (isalpha(ch) || ch == '_')
  597.             {
  598.                 /* collect the whole word */
  599.                 lex_name[0] = ch;
  600.                 for (i = 1, ch = cpp_getc();
  601.                      i < BLKSIZE - 1 && (isalnum(ch) || ch == '_');
  602.                      i++, ch = cpp_getc())
  603.                 {
  604.                     lex_name[i] = ch;
  605.                 }
  606.                 lex_name[i] = '\0';
  607.                 cpp_ungetc(ch);
  608.  
  609.                 /* is it a reserved word? */
  610.                 if (!strcmp(lex_name, "typedef"))
  611.                 {
  612.                     token = TYPEDEF;
  613.                     lex_seek = -1L;
  614.                 }
  615.                 else if (!strcmp(lex_name, "static")
  616.                       || !strcmp(lex_name, "private")
  617.                       || !strcmp(lex_name, "PRIVATE"))
  618.                 {
  619.                     token = STATIC;
  620.                     lex_seek = -1L;
  621.                 }
  622.                 else if (!strcmp(lex_name, "extern")
  623.                       || !strcmp(lex_name, "EXTERN")
  624.                       || !strcmp(lex_name, "FORWARD"))
  625.                 {
  626.                     token = EXTERN;
  627.                     lex_seek = -1L;
  628.                 }
  629.                 else
  630.                 {
  631.                     token = NAME;
  632.                     lex_seek = file_seek;
  633.                 }
  634.             }
  635.             else /* not part of a name/keyword */
  636.             {
  637.                 token = DELETED;
  638.             }
  639.  
  640.         } /* end switch(ch) */
  641.  
  642.     } while (token == DELETED);
  643.  
  644.     return token;
  645. }
  646.  
  647. /* -------------------------------------------------------------------------- */
  648. /* This is the parser.  It locates tag candidates, and then decides whether to
  649.  * generate a tag for them.
  650.  */
  651.  
  652. /* This function generates a tag for the object in lex_name, whose tag line is
  653.  * located at a given seek offset.
  654.  */
  655. void maketag(scope, seek)
  656.     int    scope;    /* 0 if global, or STATIC if static */
  657.     long    seek;    /* the seek offset of the line */
  658. {
  659.     /* output the tagname and filename fields */
  660.     if (scope == EXTERN)
  661.     {
  662.         /* whoa!  we should *never* output a tag for "extern" decl */
  663.         return;
  664.     }
  665.  
  666.     //- JAJ change the way the data is displayed
  667.     else if (scope == STATIC)
  668.     {
  669.         fprintf(tags, "%s: %-30.35s %s\t%ld\t", "static  ", lex_name, file_name, file_lnum);
  670.     }
  671.     else if (scope == BODY)
  672.     {
  673.         fprintf(tags, "%s: %-30.35s %s\t%ld\t", "function", lex_name, file_name, file_lnum);
  674.     }
  675.     else
  676.     {
  677.         fprintf(tags, "%s: %-30.35s %s\t%ld\t", "global  ", lex_name, file_name, file_lnum);
  678.     }
  679.  
  680.     /* output the target line */
  681.     putc('/', tags);
  682.     putc('^', tags);
  683.     file_copyline(seek, tags);
  684.     putc('$', tags);
  685.     putc('/', tags);
  686.     putc('\n', tags);
  687. }
  688.  
  689.  
  690. /* This function parses a source file, adding any tags that it finds */
  691. void ctags(name)
  692.     char    *name;    /* the name of a source file to be checked */
  693. {
  694.     int    prev;    /* the previous token from the source file */
  695.     int    token;    /* the current token from the source file */
  696.     int    scope;    /* normally 0, but could be a TYPEDEF or STATIC token */
  697.     int    gotname;/* boolean: does lex_name contain a tag candidate? */
  698.     long    tagseek;/* start of line that contains lex_name */
  699.  
  700.     /* open the file */
  701.     cpp_open(name);
  702.  
  703.     /* reset */
  704.     scope = 0;
  705.     gotname = FALSE;
  706.     token = SEMICOLON;
  707.  
  708.     /* parse until the end of the file */
  709.     while (prev = token, (token = lex_gettoken()) != EOF)
  710.     {
  711.         /* scope keyword? */
  712.         if (token == TYPEDEF || token == STATIC || token == EXTERN)
  713.         {
  714.             scope = token;
  715.             gotname = FALSE;
  716.             continue;
  717.         }
  718.  
  719.         /* name of a possible tag candidate? */
  720.         if (token == NAME)
  721.         {
  722.             tagseek = file_seek;
  723.             gotname = TRUE;
  724.             continue;
  725.         }
  726.  
  727.         /* if NAME BODY, without ARGS, then NAME is a struct tag */
  728.         if (gotname && token == BODY && prev != ARGS)
  729.         {
  730.             gotname = FALSE;
  731.             
  732.             /* ignore if in typedef -- better name is coming soon */
  733.             if (scope == TYPEDEF)
  734.             {
  735.                 continue;
  736.             }
  737.  
  738.             /* generate a tag, if -t and maybe -s */
  739.             if (incl_types && (file_header || incl_static))
  740.             {
  741.                 maketag(file_header ? 0 : STATIC, tagseek);
  742.             }
  743.         }
  744.  
  745.         /* If NAME ARGS BODY, then NAME is a function */
  746.         if (gotname && prev == ARGS && token == BODY)
  747.         {
  748.             gotname = FALSE;
  749.             
  750.             /* generate a tag, maybe checking -s */
  751.             if (scope != STATIC || incl_static)
  752.             {
  753. //- JAJ changed line to recognize functions
  754. //--            maketag(scope, tagseek);
  755.                 maketag(BODY, tagseek);
  756.             }
  757.         }
  758.  
  759.         /* If NAME SEMICOLON or NAME COMMA, then NAME is var/typedef */
  760.         if (gotname && (token == SEMICOLON || token == COMMA))
  761.         {
  762.             gotname = FALSE;
  763.  
  764.             /* generate a tag, if -v/-t and maybe -s */
  765.             if (scope == TYPEDEF && incl_types && (file_header || incl_static)
  766.              || scope == STATIC && incl_vars && incl_static
  767.              || incl_vars)
  768.             {
  769.                 /* a TYPEDEF outside of a header is STATIC */
  770.                 if (scope == TYPEDEF && !file_header)
  771.                 {
  772.                     maketag(STATIC, tagseek);
  773.                 }
  774.                 else /* use whatever scope was declared */
  775.                 {
  776.                     maketag(scope, tagseek);
  777.                 }
  778.             }
  779.         }
  780.  
  781.         /* reset after a semicolon or ARGS BODY pair */
  782.         if (token == SEMICOLON || (prev == ARGS && token == BODY))
  783.         {
  784.             scope = 0;
  785.             gotname = FALSE;
  786.         }
  787.     }
  788.  
  789.     /* The source file will be automatically closed */
  790. }
  791.  
  792. /* -------------------------------------------------------------------------- */
  793.  
  794. void usage()
  795. {
  796. //- JAJ make the output go to standard out so Zeus could catch it
  797.     fprintf(stdout, "usage: ctags [flags] filenames...\n");
  798.     fprintf(stdout, "\t-s  include static functions\n");
  799.     fprintf(stdout, "\t-t  include typedefs\n");
  800.     fprintf(stdout, "\t-v  include variable declarations\n");
  801.     fprintf(stdout, "\t-r  generate a \"refs\" file, too\n");
  802.     fprintf(stdout, "\t-a  append to \"tags\", instead of overwriting\n");
  803.     exit(2);
  804. }
  805.  
  806.  
  807.  
  808. #if AMIGA
  809. # include "amiwild.c"
  810. #endif
  811.  
  812. #if VMS
  813. # include "vmswild.c"
  814. #endif
  815.  
  816. main(argc, argv)
  817.     int    argc;
  818.     char    **argv;
  819. {
  820.     int    i, j;
  821.  
  822. #if MSDOS || TOS
  823.     char    **wildexpand();
  824.     argv = wildexpand(&argc, argv);
  825. #endif
  826.  
  827.     /* build the tables used by the ctype macros */
  828.     _ct_init("");
  829.  
  830.     /* parse the option flags */
  831.     for (i = 1; i < argc && argv[i][0] == '-'; i++)
  832.     {
  833.         for (j = 1; argv[i][j]; j++)
  834.         {
  835.             switch (argv[i][j])
  836.             {
  837.               case 's':    incl_static = TRUE;    break;
  838.               case 't':    incl_types = TRUE;    break;
  839.               case 'v':    incl_vars = TRUE;    break;
  840.               case 'r':    make_refs = TRUE;    break;
  841.               case 'a':    append_files = TRUE;    break;
  842.               default:    usage();
  843.             }
  844.         }
  845.     }
  846.  
  847.     /* There should always be at least one source file named in args */
  848.     if (i == argc)
  849.     {
  850.         usage();
  851.     }
  852.  
  853.     /* open the "tags" and maybe "refs" files */
  854.  
  855. //- JAJ make the output go to standard out so Zeus could catch it
  856. //    tags = fopen(TAGS, append_files ? "a" : "w");
  857.  
  858.     tags = stdout;
  859.     if (!tags)
  860.     {
  861.         perror(TAGS);
  862.         exit(3);
  863.     }
  864.     if (make_refs)
  865.     {
  866.         refs = fopen(REFS, append_files ? "a" : "w");
  867.         if (!refs)
  868.         {
  869.             perror(REFS);
  870.             //- JAJ Added error output to stdout so Zeus will see it
  871.             printf("Error opening file '%s'\n", REFS);
  872.             exit(4);
  873.         }
  874.     }
  875.  
  876.     /* parse each source file */
  877.     for (; i < argc; i++)
  878.     {
  879.         ctags(argv[i]);
  880.     }
  881.  
  882.     /* close "tags" and maybe "refs" */
  883.     fclose(tags);
  884.     if (make_refs)
  885.     {
  886.         fclose(refs);
  887.     }
  888.  
  889. #ifdef SORT
  890.         /* This is a hack which will sort the tags list.   It should
  891.          * on UNIX and OS-9.  You may have trouble with csh.   Note
  892.          * that the tags list only has to be sorted if you intend to
  893.          * use it with the real vi;  elvis permits unsorted tags.
  894.          */
  895. # if OSK
  896.         system("qsort tags >-_tags; -nx; del tags; rename _tags tags");
  897. # else    
  898.         system("sort tags >_tags$$; mv _tags$$ tags");
  899. # endif
  900. #endif
  901.  
  902.     exit(0);
  903.     /*NOTREACHED*/
  904. }
  905.  
  906. #if MSDOS || TOS
  907. # define WILDCARD_NO_MAIN
  908. # include "wildcard.c"
  909. #endif
  910.